#if !defined(_WIN32) || !defined(_WIN64)
#include <sys/types.h>
// #include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <sys/time.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "multicast.h"

#define MAX_BUFFER_LEN 1024
#define DEFAULT_MULTICAST_IP "239.0.0.1"

#if defined(_WIN32) || defined(_WIN64)
#define ERROR_INIT(sockfd, errstr)  do{   \
                                        printf("%s failed with error: %d\n",errstr, WSAGetLastError());  \
                                        close(sockfd);  \
                                        WSACleanup();   \
                                        sockfd = -1;    \
                                    } while (0);
#else
#define ERROR_INIT(sockfd, errstr)  do{   \
                                        perror(errstr);  \
                                        close(sockfd);  \
                                        sockfd = -1;    \
                                    } while (0);
#endif

#if 0
int multicast_receiver_init(char *multicast_ip, char *local_ip, uint16_t port)
{
    #if defined(_WIN32) || defined(_WIN64)
    //在windows中要使用socket，需要先注册。
    WSADATA wsaData;
    if ( WSAStartup(MAKEWORD( 2, 2 ), &wsaData ) != 0 )
    {
        printf("WSAStartup fail %d\n",WSAGetLastError());
        return -1;
    }
    #endif
    //1.创建用户数据报式套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(-1 == sockfd){
        printf("socket error");
        return -1;
    }

    //设置本地网络信息结构体
    struct sockaddr_in multicast_addr;
    memset(&multicast_addr, 0, sizeof(multicast_addr));
    multicast_addr.sin_family = AF_INET;
    if(local_ip == NULL) multicast_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    else multicast_addr.sin_addr.s_addr = inet_addr(local_ip);
    multicast_addr.sin_port = htons(port);
 
    //3.将套接字和广播信息结构体进行绑定
    if(-1 == bind(sockfd, (struct sockaddr *)&multicast_addr, sizeof(multicast_addr))){
        ERROR_INIT(sockfd, "bind");
        return -1;
    }

    //设置加入多播组
    //多播组的ip地址  224.0.0.1 – 239.255.255.255
    if(multicast_ip == NULL) multicast_ip = DEFAULT_MULTICAST_IP;
    struct ip_mreq my_mreq;
    memset(&my_mreq, 0, sizeof(my_mreq));
    my_mreq.imr_multiaddr.s_addr = inet_addr(multicast_ip);
    if(local_ip == NULL) my_mreq.imr_interface.s_addr = INADDR_ANY; 
    else my_mreq.imr_interface.s_addr = inet_addr(local_ip);
    if(-1 == setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&my_mreq, sizeof(my_mreq))){
        ERROR_INIT(sockfd, "setsockopt1");
        return -1;
    }

    // 设置接收组播信息，不接收单播信息
    int loopback = 0; // 0表示禁止接收自己发送的组播信息，1表示允许接收
    if (setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, (const char*)&loopback, sizeof(loopback)) == -1)
    {
        ERROR_INIT(sockfd, "setsockopt2");
        return 1;
    }
 
    //定义一个结构体，保存客户端的信息
    struct sockaddr_in remote_addr;
    memset(&remote_addr, 0, sizeof(remote_addr));//清空
    socklen_t clientaddrlen = sizeof(remote_addr);
 
    char buff[MAX_BUFFER_LEN] = {0};
 
    while(1){
        //接收数据，如果想要给对方回应，就必须保存对方的网络信息结构体
        //如果不回应，后两个参数写 NULL 也行
        if(-1 == recvfrom(sockfd, buff, MAX_BUFFER_LEN, 0, (struct sockaddr *)&remote_addr, &clientaddrlen)){
            printf("recvfrom error");
            return -1;
        }
        printf("%s(%d):%s\n", inet_ntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port), buff);
        
        #if 0
        //组装应答信息
        strcat(buff, "--sever");
        if(-1 == sendto(sockfd, buff, N, 0, (struct sockaddr *)&remote_addr, clientaddrlen)){
            printf("sendto error");
        }
        #endif
        memset(buff, 0, MAX_BUFFER_LEN);
    }
    
    //关闭套接字
    close(sockfd);
 
    return 0;
}

int multicast_sender_init(char *multicast_ip, char *local_ip, uint16_t port)
{
    #if defined(_WIN32) || defined(_WIN64)
    //在windows中要使用socket，需要先注册。
    WSADATA wsaData;
    if ( WSAStartup( MAKEWORD( 2, 2 ), &wsaData ) != 0 )
    {
        printf("WSAStartup fail %d\n",WSAGetLastError());
        return -1;
    }
    #endif
    //创建用户数据报套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(-1 == sockfd){
        printf("socket error");
        return -1;
    }
 
    //填充服务器组播信息结构体
    if(multicast_ip == NULL) multicast_ip = DEFAULT_MULTICAST_IP;
    struct sockaddr_in multicast_addr;
    memset(&multicast_addr, 0, sizeof(multicast_addr));
    multicast_addr.sin_family = AF_INET;
    multicast_addr.sin_port = htons(port);
    //多播组的ip地址  224.0.0.1 – 239.255.255.255
    //这个范围内的每个ip地址 都表示一个多播组
    multicast_addr.sin_addr.s_addr = inet_addr(multicast_ip);
 
    char buff[MAX_BUFFER_LEN] = {0};
    while(1){
        printf("input your msg:");
        fgets(buff, MAX_BUFFER_LEN, stdin);
        buff[strlen(buff)-1] = '\0';//清除 \n
        if(0 == strcmp(buff, "quit")){
            break;
        }
        if(-1 == sendto(sockfd, buff, MAX_BUFFER_LEN, 0, (struct sockaddr *)&multicast_addr, sizeof(multicast_addr))){
            printf("sendto error");
            return -1;
        }
        memset(buff, 0, MAX_BUFFER_LEN);
    }
    //关闭套接字
    close(sockfd);
 
    return 0;

}
#endif
int multicast_join(struct multicast_handle *mch, char *multicast_ip, char *local_ip, uint16_t port) {
    if(!mch) return -1;
    #if defined(_WIN32) || defined(_WIN64)
    //在windows中要使用socket，需要先注册。
    WSADATA wsaData;
    if ( WSAStartup(MAKEWORD( 2, 2 ), &wsaData ) != 0 )
    {
        printf("WSAStartup fail %d\n",WSAGetLastError());
        return -1;
    }
    #endif
    //1.创建用户数据报式套接字
    mch->sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(-1 == mch->sockfd){
        printf("socket error");
        return -1;
    }

    //设置本地网络信息结构体
    struct sockaddr_in local_addr;
    memset(&local_addr, 0, sizeof(local_addr));
    local_addr.sin_family = AF_INET;
    if(local_ip == NULL) local_addr.sin_addr.s_addr = htons(INADDR_ANY);
    else local_addr.sin_addr.s_addr = inet_addr(local_ip);
    local_addr.sin_port = htons(port);
    //3.将套接字和广播信息结构体进行绑定
    if(-1 == bind(mch->sockfd, (struct sockaddr *)&local_addr, sizeof(local_addr))) {
        ERROR_INIT(mch->sockfd, "bind");
        return -1;
    }

    //设置加入多播组
    //多播组的ip地址  224.0.0.1 – 239.255.255.255
    if(multicast_ip == NULL) multicast_ip = DEFAULT_MULTICAST_IP;
    // struct ip_mreq my_mreq;
    memset(&mch->my_mreq, 0, sizeof(mch->my_mreq));
    mch->my_mreq.imr_multiaddr.s_addr = inet_addr(multicast_ip);
    if(local_ip == NULL) mch->my_mreq.imr_interface.s_addr = htons(INADDR_ANY); 
    else mch->my_mreq.imr_interface.s_addr = inet_addr(local_ip);
    if(-1 == setsockopt(mch->sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mch->my_mreq, sizeof(mch->my_mreq))){
        ERROR_INIT(mch->sockfd, "setsockopt1");
        return -1;
    }

    // 设置接收组播信息，不接收单播信息
    int loopback = 0; // 0表示禁止接收自己发送的组播信息，1表示允许接收
    if (setsockopt(mch->sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, (const char*)&loopback, sizeof(loopback)) == -1) {
        ERROR_INIT(mch->sockfd, "setsockopt2");
        return 1;
    }
 
    //定义一个结构体，保存客户端的信息
    memset(&mch->remote_addr, 0, sizeof(mch->remote_addr));//清空

    //填充服务器组播信息结构体
    // struct sockaddr_in multicast_addr;
    memset(&mch->multicast_addr, 0, sizeof(mch->multicast_addr));
    mch->multicast_addr.sin_family = AF_INET;
    mch->multicast_addr.sin_port = htons(port);
    //多播组的ip地址  224.0.0.1 – 239.255.255.255
    //这个范围内的每个ip地址 都表示一个多播组
    mch->multicast_addr.sin_addr.s_addr = inet_addr(multicast_ip);

    return mch->sockfd;
}

int multicast_send(struct multicast_handle *mch, char* buff, int buflen)
{
    if(!mch || mch->sockfd==-1 || !buff || !buflen)  return -1;
    return sendto(mch->sockfd, buff, buflen, 0, (struct sockaddr *)&mch->multicast_addr, sizeof(mch->multicast_addr));
}

int multicast_recv(struct multicast_handle *mch, char* buff, int buflen)
{
    if(!mch || mch->sockfd==-1 || !buff || !buflen)  return -1;
    socklen_t ss = sizeof(mch->remote_addr);
    return recvfrom(mch->sockfd, buff, buflen, 0, (struct sockaddr *)&mch->remote_addr, &ss);
}

int multicast_close(struct multicast_handle *mch)
{
    if(setsockopt(mch->sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP,(char*)&mch->my_mreq,sizeof(mch->my_mreq)) == -1)  /*退出广播组*/
    {
        perror("setsockopt");
        return -1;
    }
    close(mch->sockfd);
    mch->sockfd = -1;
    return 0;
}   

static void recv_demo()
{
    struct multicast_handle mch;
    char buff[2048];
    int len;

    if(multicast_join(&mch, "224.1.0.1", NULL, 8888) < 0)  //172.25.208.198
    {
        return;
    }
    len = multicast_send(&mch, buff, 2048);
    if(len <= 0 )
    {
        printf("send fail: %d\n",len);
    }

    memset(buff,0,2048);
    while (1)
    {
        len = multicast_recv(&mch, buff, 2048);
        if(len <= 0 )
        {
            printf("receive fail: %d\n",len);
        }
        if(buff[0] == 'q') break;
        printf("%s(%d):(%d)%s\n", inet_ntoa(mch.remote_addr.sin_addr), ntohs(mch.remote_addr.sin_port), len, buff);
        memset(buff,0,2048);
    }

    multicast_close(&mch);
}

static void send_demo()
{
    struct multicast_handle mch;
    char buff[2048];
    int len;

    if(multicast_join(&mch, "224.1.0.1", NULL, 8888) < 0)
    {
        return;
    }
    memset(buff,0,2048);
    while (1)
    {
        printf("input your msg:");
        fgets(buff, 2048, stdin);
        len = multicast_send(&mch, buff, 2048);
        if(len <= 0 )
        {
            printf("send fail: %d\n",len);
        }
        if(buff[0] == 'q') break;
    }

    multicast_close(&mch);
}

